#!/usr/bin/env python

from ola.ClientWrapper import ClientWrapper
import argparse
import subprocess
import socket
import sys
from boola import boola
import time

"""
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Library General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


  Boola 1.0.0 (Banshee control Over OLA)  
  (c) 2011 Chris Stranex. All Rights Reserved.   
"""


if __name__ == '__main__':
   class Main:
   
      old = [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1];
      position = {
         'old': (-1,-1),
         'current': (0,0),
      };
   
      def __init__(self,address,mode,universe):
         self.wrapper = ClientWrapper()
         self.client = self.wrapper.Client()
         self.client.RegisterUniverse(universe, self.client.REGISTER, self.dmx)
         self.banshee = boola.Banshee();
         self.mode = mode;
         self.address = address;
         self.actions = {
            0: self.banshee.setVolume,
            1: self.banshee.setPan,
            2: self.setPlayback,
            3: self.changeLSB,
            4: self.changeMSB,
            5: lambda x: self.banshee.setTrack(x-1), # DMX Range: 1 - 255. Track Range: 0 - 254. (DMX value 0 ignored) 
            6: lambda x: None,
            7: lambda x: None,
            8: self.banshee.setPlaylist, # Playlist Range: 1 - 255. Playlist Range: 1 - 256. (DMX value below 1 ignored)
            9: self.setControl,
         };
   
      
      def start(self):
         self.wrapper.Run();
      
      def dmx(self,data):
         try:
            data = data[self.address-1:self.address-1+self.mode];
            if debug:
               if data != self.old:
                  print 'raw dmx', data;
         except IndexError:
            if debug:
               print "Data not in range";
            return;
   
         # Data!
         for i in range(0,len(data)):
            if not self.old[i] == data[i]:
               self.actions[i](data[i]);
               self.old[i] = data[i];
   
         if self.position['old'] != self.position['current']:
            self.setPosition();
   
      def setControl(self,value):
         if value >= 0 and value <= 19:
            # No Function
            return;
         elif value >= 20 and value <= 39:
            # Shuffle by Song
            self.banshee.setShuffle(boola.SHUFFLE_MODE_ON);
         elif value >= 40 and value <= 59:
            self.banshee.setShuffle(boola.SHUFFLE_MODE_OFF);
         elif value >= 60 and value <= 79:
            self.banshee.setRepeat(boola.REPEAT_MODE_ALL);
         elif value >= 80 and value <= 99:
            self.banshee.setRepeat(boola.REPEAT_MODE_ONE);
         elif value >= 100 and value <= 119:
            self.banshee.setRepeat(boola.REPEAT_MODE_OFF);
         elif value >= 120 and value <= 139:
            self.banshee.setStopAfterCurrent(True);
         elif value >= 140 and value <= 159:
            self.banshee.setStopAfterCurrent(False);
         elif value >= 160:
            # Reserved;
            return;
   
      def setPlayback(self,value):
         if value >= 0 and value <= 15:
            # Stop
            self.banshee.Stop();
         elif value >= 16 and value <= 31:
            # Pause
            self.banshee.Pause();
         elif value >= 32 and value <= 47:
            # Play
            self.banshee.Play();
         elif value >= 48 and value <= 63:
            # Continue
            self.banshee.Continue();
         elif value >= 64:
            # Reserved
            return;
   
      def setPosition(self):
         # Convert (msb,lsb) tuple into a 16-bit integer
         self.position['old'] = self.position['current'];
         msb = bin(self.position['current'][0])[2:];
         lsb = bin(self.position['current'][1])[2:];
         if len(msb) < 8: 
            msb = "0".zfill(8-len(msb)) + msb;
         if len(lsb) < 8:
            lsb = "0".zfill(8-len(lsb)) + lsb;   
         self.banshee.setPosition(int("%s%s" % (msb,lsb),2));
   
      def changeLSB(self,x):
         self.position['current'] = (self.position['current'][0],x);
      
      def changeMSB(self,x):
         self.position['current'] = (x,self.position['current'][1]);
      

   class DMXAddress(int):
      def __init__(self,n):
         if type(n) != type(1):
            n = int(n);
         if n > 502:
            raise ValueError("Cannot have a value higher than 502");
         elif n < 1:
            raise ValueError("Cannot have a value lower than 1");
         else:
            self.n = n;
      def __int__(self):
         return self.n;

   parser = argparse.ArgumentParser(prog="boola", description='Banshee control Over OLA')
   parser.add_argument('-v','--version', action='version', version=('%(prog)s ' + boola.VERSION),help="Displays the version information")
   parser.add_argument('-a',action='store',default=[1],dest="address",nargs=1,type=DMXAddress,help="Start address (1-502). Default: 1");
   parser.add_argument('-d','--debug',action='store_true',default=False,dest="debug",help="Debug mode");
   parser.add_argument('-u',action='store',default=[0],dest="universe",nargs=1,type=int,help="Select the OLA universe to receive DMX from. Default: 0");
   parser.add_argument('-w','--winamp',action='store_const',const=boola.WINAMP_MODE,default=boola.ADVANCED_MODE,dest='mode',help="Use the Winamp fixture mode");

   values = parser.parse_args();
   debug = values.debug;
   boola.debug = values.debug;
   
   if debug:
      print "Starting in debug mode";

   try:
      app = Main(values.address[0],values.mode,values.universe[0]);
   except RuntimeError:
      time.sleep(1);
      try:
         app = Main(values.address[0],values.mode,values.universe[0]);
      except RuntimeError as e:
         print "Error:", e;
         sys.exit(1);
   except socket.error:
      print "Error: Could not connect to OLA. Is it running?";
      sys.exit(1);
   
   if debug:
      print "Starting boola";
   app.start();
